import re

from PySide6.QtCore import Slot, Qt, QSize, QFile, QIODevice, QTextStream, Signal, QStringConverter
from PySide6.QtGui import QFont, QKeyEvent, QIcon
from PySide6.QtWidgets import QListWidgetItem, QWidget, QSizePolicy, QVBoxLayout, QApplication, QHBoxLayout, QListWidget

from keyboard import KeyButton, AbstractKeyboard
from keyboard.key_button import Mode

BUTTON_WIDTH_RATIO = 0.09
BUTTON_HEIGHT_RATIO = 0.2
BACKSPACE_ICON = './assets/Image/backspace.png'
CAPLOCK_ICON = './assets/Image/caplock.png'
ENTER_ICON = './assets/Image/enter.png'
SPACE_ICON = './assets/Image/space.png'

class ChineseWidget(QListWidget):
  pressedChanged = Signal(int, str)
  def __init__(self, parent=None):
    super().__init__(parent)
    self.m_data = {}
    self.setFocusPolicy(Qt.NoFocus)
    self.setViewMode(QListWidget.ListMode)
    self.setFlow(QListWidget.LeftToRight)
    self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
    self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
    self.setHorizontalScrollMode(QListWidget.ScrollPerPixel)
    self.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)
    self.setStyleSheet(
      '''
      QListWidget { background-color: #2B2D30; outline: none; border:1px solid #00000000; color: #e8e8e8; }
      QListWidget::Item { width: 50px; height: 50px; }
      QListWidget::Item:hover { background-color: #b5b6b7; color: white; }
      QListWidget::item:selected { background-color: #4395ff; color: white; }
      QListWidget::item:selected:!active { background-color: #00000000; color: white; }
      '''
    )
    self.itemClicked.connect(self.onItemClicked)
    self.loadGoogleChineseLib()

  def setText(self, text):
    # 清空现有项目
    self.clear()
    # 添加新的项目
    self.addOneItem(text)
    # 检查 text 是否为空字符串
    if not text:
        return
    if text[0] not in self.m_data:
        return
    # 获取词库内容进行匹配
    tmp = self.m_data[text[0]]
    for each in tmp:
        if not each[0].startswith(text):
            continue
        # 添加到候选栏，限制数量
        if self.count() <= 30:
            self.addOneItem(each[1])
        else:
            break

  @Slot(QListWidgetItem)
  def onItemClicked(self, item):
    self.pressedChanged.emit(-1, item.text())
    self.setText("")

  def addOneItem(self, text):
    item = QListWidgetItem(text, self)
    font = QFont()
    font.setPointSize(18)
    font.setBold(True)
    item.setFont(font)
    item.setTextAlignment(Qt.AlignCenter)

    isChinese = re.match(r"^[\u4E00-\u9FA5]+", text[:1])
    width = font.pointSize()
    width += len(text) * font.pointSize() * (1.5 if isChinese else 2 / 3)
    item.setSizeHint(QSize(width, 50))
    self.addItem(item)

  class ChineseWidget(QWidget):
    def __init__(self, parent=None):
      super().__init__(parent)
      self.m_data = {}

    def loadChineseLib(self):
      '''加载中文字库'''
      pinyin = QFile('./assets/ChineseLib/pinyin.txt')
      if not pinyin.open(QIODevice.ReadOnly):
        print("Open pinyin file failed!")
        return
      while not pinyin.atEnd():
        buf = str(pinyin.readLine(), 'utf-8').strip()
        match = re.match(r"^[\u4E00-\u9FA5]+", buf)
        if not match:
          continue
        first = buf[match.end():]
        second = match.group(0)
        if first[0] not in self.m_data:
          self.m_data[first[0]] = []
        self.m_data[first[0]].append((first, second))
      pinyin.close()

  def loadChinesePhraseLib(self):
    '''加载中文词库'''
    pinyin = QFile("./assets/ChinesePhraseLib/pinyin_phrase.txt")
    if not pinyin.open(QIODevice.ReadOnly):
      print("Open pinyin file failed!")
      return
    # 按行读取内容
    while not pinyin.atEnd():
      buf = str(pinyin.readLine(), 'utf-8').strip()
      if not buf or buf.startswith("#"):
        continue
      # 正则匹配
      reg_exp = re.compile(r"(\S+): ([\S ]+)")
      match = reg_exp.match(buf)
      if match:
        second = match.group(1)
        first = match.group(2)
        str_list = first.split(" ")
        abb = ''.join([s[0] for s in str_list])
        if first[0] not in self.m_data:
          self.m_data[first[0]] = []
        # 更新数据
        self.m_data[first[0]].append((abb, second))
        self.m_data[first[0]].append((first.replace(" ", ""), second))
    pinyin.close()

  def loadGoogleChineseLib(self):
    '''加载谷歌中文词库'''
    file = QFile("./assets/GoogleChineseLib/rawdict_utf16_65105_freq_sort.txt")
    if not file.open(QIODevice.ReadOnly):
      print(f"Open pinyin file failed! {file.fileName()}")
      return
    in_stream = QTextStream(file)
    in_stream.setEncoding(QStringConverter.Encoding.Utf16)
    lines = in_stream.readAll().split("\n")
    for each in lines:
      re_pattern = re.compile(r"(\S+).((?:-?\d+)(?:\.\d+)).((?:-?\d+)(?:\.\d+)?).(.*)")
      match = re_pattern.search(each)
      if match and match.lastindex == 4:
        hanzi = match.group(1)
        pinyin = match.group(4)
        str_list = pinyin.split(" ")
        abb = ''.join([s[0] for s in str_list])
        if pinyin[0] not in self.m_data:
          self.m_data[pinyin[0]] = []
        if len(str_list) > 1:
          self.m_data[pinyin[0]].append((abb, hanzi))
        self.m_data[pinyin[0]].append((pinyin.replace(" ", ""), hanzi))
      else:
        print(each)
    file.close()



class Keyboard(AbstractKeyboard):
  modeListBar1 = [
    [Mode(Qt.Key_Q, "q"), Mode(Qt.Key_Q, "Q"), Mode(Qt.Key_1, "1")],
    [Mode(Qt.Key_W, "w"), Mode(Qt.Key_W, "W"), Mode(Qt.Key_2, "2")],
    [Mode(Qt.Key_E, "e"), Mode(Qt.Key_E, "E"), Mode(Qt.Key_3, "3")],
    [Mode(Qt.Key_R, "r"), Mode(Qt.Key_R, "R"), Mode(Qt.Key_4, "4")],
    [Mode(Qt.Key_T, "t"), Mode(Qt.Key_T, "T"), Mode(Qt.Key_5, "5")],
    [Mode(Qt.Key_Y, "y"), Mode(Qt.Key_Y, "Y"), Mode(Qt.Key_6, "6")],
    [Mode(Qt.Key_U, "u"), Mode(Qt.Key_U, "U"), Mode(Qt.Key_7, "7")],
    [Mode(Qt.Key_I, "i"), Mode(Qt.Key_I, "I"), Mode(Qt.Key_8, "8")],
    [Mode(Qt.Key_O, "o"), Mode(Qt.Key_O, "O"), Mode(Qt.Key_9, "9")],
    [Mode(Qt.Key_P, "p"), Mode(Qt.Key_P, "P"), Mode(Qt.Key_0, "0")]
  ]

  modeListBar2 = [
    [Mode(Qt.Key_A, "a"), Mode(Qt.Key_A, "A"), Mode(Qt.Key_unknown, ".")],
    [Mode(Qt.Key_S, "s"), Mode(Qt.Key_S, "S"), Mode(Qt.Key_unknown, "?")],
    [Mode(Qt.Key_D, "d"), Mode(Qt.Key_D, "D"), Mode(Qt.Key_At, "!")],
    [Mode(Qt.Key_F, "f"), Mode(Qt.Key_F, "F"), Mode(Qt.Key_NumberSign, "*")],
    [Mode(Qt.Key_G, "g"), Mode(Qt.Key_G, "G"), Mode(Qt.Key_Percent, "#")],
    [Mode(Qt.Key_H, "h"), Mode(Qt.Key_H, "H"), Mode(Qt.Key_unknown, "\"")],
    [Mode(Qt.Key_J, "j"), Mode(Qt.Key_J, "J"), Mode(Qt.Key_unknown, "&")],
    [Mode(Qt.Key_K, "k"), Mode(Qt.Key_K, "K"), Mode(Qt.Key_unknown, "%")],
    [Mode(Qt.Key_L, "l"), Mode(Qt.Key_L, "L"), Mode(Qt.Key_unknown, "@")]
  ]

  modeListBar3 = [
    [Mode(Qt.Key_CapsLock, "", "")],
    [Mode(Qt.Key_Z, "z"), Mode(Qt.Key_Z, "Z"), Mode(Qt.Key_ParenLeft, "(")],
    [Mode(Qt.Key_X, "x"), Mode(Qt.Key_X, "X"), Mode(Qt.Key_ParenLeft, ")")],
    [Mode(Qt.Key_C, "c"), Mode(Qt.Key_C, "C"), Mode(Qt.Key_Minus, "-")],
    [Mode(Qt.Key_V, "v"), Mode(Qt.Key_V, "V"), Mode(Qt.Key_unknown, "_")],
    [Mode(Qt.Key_B, "b"), Mode(Qt.Key_B, "B"), Mode(Qt.Key_unknown, ":")],
    [Mode(Qt.Key_N, "n"), Mode(Qt.Key_N, "N"), Mode(Qt.Key_Semicolon, ";")],
    [Mode(Qt.Key_M, "m"), Mode(Qt.Key_M, "M"), Mode(Qt.Key_Slash, "/")],
    [Mode(Qt.Key_Backspace, "", "")]
  ]

  modeListBar4 = [
    [Mode(Qt.Key_Mode_switch, "", "?123")],
    [Mode(Qt.Key_Context1, "", "En"), Mode(Qt.Key_Context1, "", "中")],
    [Mode(Qt.Key_Space, " ", "")],
    [Mode(Qt.Key_Enter, "", "")]
  ]
  def __init__(self, parent=None):
    super().__init__(parent)
    self.m_isChinese = False
    self.m_bufferText = ''
    self.button_flags = {}
    self.setStyleSheet('''
        QWidget {
            background-color: #2B2D30;
        }
    ''')
    self.m_chineseWidget = ChineseWidget(self)
    self.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)
    self.setFixedSize(850, 320)
    self.resizeButton()

    layout = QVBoxLayout(self)
    layout.setSpacing(0)
    layout.setContentsMargins(0, 0, 0, 0)

    layout.addWidget(self.m_chineseWidget, 12)
    layout.addLayout(self.h1(), 15)
    layout.addLayout(self.h2(), 15)
    layout.addLayout(self.h3(), 15)
    layout.addLayout(self.h4(), 15)

    self.setLayout(layout)

    self.m_chineseWidget.pressedChanged.connect(self.onKeyPressed)
    self.m_chineseWidget.pressedChanged.connect(self.clearBufferText)

  def resizeButton(self):
    for button in self.findChildren(KeyButton):
      fixed_width = self.width() * BUTTON_WIDTH_RATIO
      fixed_height = self.height() * BUTTON_HEIGHT_RATIO
      button.setIconSize(QSize(2 * fixed_width // 3, 2 * fixed_height // 3))

      if button not in self.button_flags:
        self.button_flags[button] = {
          'caps_lock_connected': False,
          'special_char_connected': False,
          'en_or_ch_connected': False,
        }

      if button.mode().key == Qt.Key_Backspace:
        button.setIcon(QIcon(BACKSPACE_ICON))

      elif button.mode().key == Qt.Key_CapsLock:
        button.setIcon(QIcon(CAPLOCK_ICON))
        if not self.button_flags[button]['caps_lock_connected']:
          button.pressed.connect(self.switchCapsLock)
          self.button_flags[button]['caps_lock_connected'] = True

      elif button.mode().key == Qt.Key_Mode_switch:
        if not self.button_flags[button]['special_char_connected']:
          button.pressed.connect(self.switchSpecialChar)
          self.button_flags[button]['special_char_connected'] = True

      elif button.mode().key == Qt.Key_Context1:
        if not self.button_flags[button]['en_or_ch_connected']:
          button.pressed.connect(self.switchEnOrCh)
          self.button_flags[button]['en_or_ch_connected'] = True

      elif button.mode().key == Qt.Key_Enter:
        button.setIcon(QIcon(ENTER_ICON))

      elif button.mode().key == Qt.Key_Space:
        button.setIcon(QIcon(SPACE_ICON))

  def resizeEvent(self, event):
    self.resizeButton()

  @Slot()
  def switchCapsLock(self):
    for button in self.findChildren(KeyButton):
      button.switchCapsLock()

  @Slot()
  def switchSpecialChar(self):
    for button in self.findChildren(KeyButton):
      button.switchSpecialChar()

  @Slot()
  def switchEnOrCh(self):
    self.m_isChinese = not self.m_isChinese
    for button in self.findChildren(KeyButton):
      if button.mode().key == Qt.Key_Context1:
        button.switching()

  @Slot(int, str)
  def onButtonPressed(self, code, text):
    if not self.m_isChinese:
      self.onKeyPressed(code, text)
      self.m_bufferText = ''
      return

    if not re.match(r'[a-zA-Z]', text) and not self.m_bufferText:
      self.onKeyPressed(code, text)
      return

    if code == Qt.Key_Backspace:
      self.m_bufferText = self.m_bufferText[:-1]
    else:
      self.m_bufferText += text

    self.m_chineseWidget.setText(self.m_bufferText)

  @Slot(int, str)
  def onKeyPressed(self, code, text):
    receiver = QApplication.focusWidget()
    if receiver is not None:
      key_press = QKeyEvent(QKeyEvent.KeyPress, code, Qt.NoModifier, text)
      key_release = QKeyEvent(QKeyEvent.KeyRelease, code, Qt.NoModifier, text)
      QApplication.sendEvent(receiver, key_press)
      QApplication.sendEvent(receiver, key_release)

  @Slot()
  def clearBufferText(self):
    self.m_bufferText = ""

  def createButton(self, modes):
    button = KeyButton(modes, self)
    button.onReponse(self, self.onButtonPressed)
    button.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)
    return button

  def createBar(self, modes):
    widget = QWidget()
    h_layout = QHBoxLayout(widget)
    for mode_list in modes:
      button = self.createButton(mode_list)
      h_layout.addWidget(button)

  def h1(self):
    h = QHBoxLayout()
    h.setSizeConstraint(QHBoxLayout.SetNoConstraint)
    for modes in self.modeListBar1:
      button = self.createButton(modes)
      h.addWidget(button)
    return h

  def h2(self):
    h = QHBoxLayout()
    h.addSpacing(20)
    for modes in self.modeListBar2:
      button = self.createButton(modes)
      h.addWidget(button)
    h.addSpacing(20)
    return h

  def h3(self):
    h = QHBoxLayout()
    h.setSpacing(0)
    for i, modes in enumerate(self.modeListBar3):
      button = self.createButton(modes)
      if i == 0 or i == (len(self.modeListBar3) - 1):
        h.addWidget(button, 70)
      else:
        h.addWidget(button, 69)
    return h

  def h4(self):
    h = QHBoxLayout()
    h.setSpacing(0)
    for i, modes in enumerate(self.modeListBar4):
      button = self.createButton(modes)
      if i == 0:
        h.addWidget(button, 12)
      elif i == 1:
        h.addWidget(button, 10)
      elif i == 2:
        h.addWidget(button, 56)
      elif i == 3:
        h.addWidget(button, 22)
    return h